iT邦幫忙

2024 iThome 鐵人賽

DAY 16
0

昨天我們在 RN裡面使用 fetch來串接 API和後端溝通註冊。但其實在實際上使用 RN串接API的時候不會只用一個 fetch 直接來串接這樣。

會用一些狀態管理,Redux的概念來做著狀態的管理。但這樣又多了一個東西什麼是 Redux。

Redux

感覺又多介紹一個專有的東西怕嚇到人,但在我們本次挑戰之中,概念比實作重要。畢竟我們到現在還沒親自寫過什麼 Code🤣。

介紹

Redux 是一個專為管理應用程式狀態的 JavaScript 套。當開發應用程式時,常常會遇到跨多個元件或頁面分享和管理狀態的情況,這時 Redux 就能提供幫助。它的核心概念是將應用程式的狀態集中存放在一個「Store」(商店?[註一])中,並透過「Actions」(動作)和「Reducers」(篩子)來更新這個狀態。

其實我覺得 Redux有點複雜,新手在使用的時候也會不太清楚怎麼用。我當初其實是用了好一大段時間,也只是大概好像知道是怎麼一回事。但可以先做出來可以先動會再來了解我覺得也是一個不錯的學習方法。

概念

我們來簡單看一下 Redux的概念。一個簡單的 UI 動作來說明 Redux 的工作流程,例如「按鈕點擊後改變畫面上的顏色」這個場景。

  1. 用戶點擊按鈕(Action)
    當點擊按鈕時,會觸發一個 action,也就是通知 Redux 有些東西要發生了。這個 action 通常是一個描述事件的 JavaScript 對象,例如:
const changeColorAction = {
    type: 'CHANGE_COLOR',
    payload: 'blue',
};

這個 action 的 type 是「CHANGE_COLOR」,代表要改變顏色的一個事件,而 payload 裡面存放的是要改成的顏色「blue」。

  1. 發送 Action 到 Store(Dispatch)
    點擊按鈕後,我們會通過 Redux 的 dispatch 函數將這個 action 發送到 store
dispatch(changeColorAction);

Redux 的 dispatch 函數就像是一個foodpand 送餐員,將這個 changeColorAction 傳送到 store 中,讓 store的狀態改變

  1. Reducer 處理 Action 並更新 State
    store 中,reducer 負責接收 action 並更新應用的 state(狀態)。Reducer 是一個純函數,根據 action 的 type 來決定如何改變 state:
const initialState = { color: 'red' };

function colorReducer(state = initialState, action) {
    switch (action.type) {
        case 'CHANGE_COLOR':
            return { ...state, color: action.payload };
        default:
            return state;
    }
}

當 reducer 接收到 CHANGE_COLOR 的 action,它會將顏色更新為 action 中的 payload 值,這樣 state 中的顏色就會從綠色變成藍色。

  1. UI 根據 State 變化而更新

Redux 的 store 會通知應用程式中使用該 state 的所有部分,然後 UI 會根據新的 state 做出變化,例如改變顏色:

const color = useSelector((state) => state.color);

return (
    <div style={{ backgroundColor: color }}>
        <button onClick={() => dispatch(changeColorAction)}>Change Color</button>
    </div>
);

這樣,用戶每次點擊按鈕時,背景顏色都會根據更新後的 state 變成藍色。

為什麼要使用 Redux?

  1. 集中管理狀態:當APP或網頁寫得越來越大變得複雜時,狀態(State)管理會變得更加困難。Redux 提供一個集中式的「Store」。不同功能模組的狀態都在這邊管理。
  2. 可預測的狀態更新:Redux 使用「純函數」Reducers來更新狀態,這意味著同樣的輸入永遠會得到相同的輸出,這使應用程式的行為變得更容易預測。
  3. 工具支援:Redux 的 DevTools 讓你能夠追蹤每一個狀態更新、動作歷史,甚至可以進行時間旅行調試,這對於除錯和分析非常有幫助。

註一:我以前看的書都說商店是 Store,不確定是不是因為我看的是中國的書的原因。

Redux Toolkit

其實用來實作 Redux 有滿多工具的,常見的有 redux saga , redux toolkit ...等。也有比較簡單的配置直接用 redux thunk 之類的。

我這邊主要看過 Saga和 toolkit的實作。Saga我個人覺得滿複雜的,學習成本稍微高一點。Toolkit我覺得就很適合新手學習。程式碼也比較少一點比較簡單,我們這邊就選擇使用 redux toolkit實作。

實作

雖然前面介紹了一堆概念,但我們其實只需要在 Cursor中和AI說請幫我加上 toolkit 的配置,應該就可以了,我們來試試看。

加上 Redux

幫我加上 redux ,我想用 redux toolkit 這個套件。並且改寫註冊這個API

[!Info] GPT回應
要在你的 React Native 專案中加入 Redux Toolkit,並改寫註冊 API 的邏輯,你可以按照以下步驟進行:

1. 安裝 Redux Toolkit 和 React-Redux

首先,你需要安裝 @reduxjs/toolkitreact-redux

yarn add @reduxjs/toolkit react-redux

2. 設定 Redux Store

在專案中創建一個新的文件夾 store,並在裡面創建一個 store.js 文件:

import { configureStore } from '@reduxjs/toolkit';
import registerReducer from './registerSlice';

const store = configureStore({
    reducer: {
        register: registerReducer,
    },
});

export default store;

3. 創建 Slice

store 文件夾中創建一個 registerSlice.js 文件,並添加以下代碼:

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

export const registerUser = createAsyncThunk(
    'register/registerUser',
    async (userData, { rejectWithValue }) => {
        try {
            const response = await fetch('http://localhost:3000/auth/register', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify(userData),
            });

            if (!response.ok) {
                const data = await response.json();
                return rejectWithValue(data.message || '註冊失敗,請稍後再試。');
            }

            return await response.json();
        } catch (error) {
            return rejectWithValue('註冊時發生錯誤,請檢查您的網路連接。');
        }
    }
);

const registerSlice = createSlice({
    name: 'register',
    initialState: {
        error: null,
        success: false,
    },
    reducers: {
        clearError: (state) => {
            state.error = null;
        },
    },
    extraReducers: (builder) => {
        builder
            .addCase(registerUser.fulfilled, (state) => {
                state.success = true;
                state.error = null;
            })
            .addCase(registerUser.rejected, (state, action) => {
                state.error = action.payload;
            });
    },
});

export const { clearError } = registerSlice.actions;
export default registerSlice.reducer;

4. 更新 App 組件

App.tsx 中,將 Redux Store 包裹在應用中:

import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { NativeBaseProvider } from 'native-base';
import { Provider } from 'react-redux';
import store from './store/store';
import Login from './components/Login';
import Register from './components/Register';
import Homepage from './components/Homepage';
import CreateReport from './components/CreateReport';

const Stack = createNativeStackNavigator();

const App = () => {
    return (
        <Provider store={store}>
            <NativeBaseProvider>
                <NavigationContainer>
                    <Stack.Navigator initialRouteName="Login">
                        <Stack.Screen name="Login" component={Login} />
                        <Stack.Screen name="Register" component={Register} />
                        <Stack.Screen name="Homepage" component={Homepage} />
                        <Stack.Screen name="CreateReport" component={CreateReport} />
                    </Stack.Navigator>
                </NavigationContainer>
            </NativeBaseProvider>
        </Provider>
    );
};

export default App;

5. 更新 Register 組件

最後,更新 Register.tsx 以使用 Redux Toolkit 的 registerUser

import React, { useState } from 'react';
import { NativeBaseProvider, Box, Input, Button, Text } from 'native-base';
import { Alert } from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import { registerUser, clearError } from '../store/registerSlice';

const Register = ({ navigation }) => {
    const dispatch = useDispatch();
    const { error } = useSelector((state) => state.register);
    const [username, setUsername] = useState('');
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const [confirmPassword, setConfirmPassword] = useState('');
    const [account, setAccount] = useState('');

    const handleRegister = () => {
        if (!username || !email || !password || !confirmPassword || !account) {
            Alert.alert('所有欄位都是必填的');
            return;
        }

        if (password !== confirmPassword) {
            Alert.alert('密碼和確認密碼不一致');
            return;
        }

        dispatch(registerUser({ username, email, password, account }));
    };

    React.useEffect(() => {
        if (error) {
            Alert.alert('註冊失敗', error, [{ text: '確定', onPress: () => dispatch(clearError()) }]);
        }
    }, [error, dispatch]);

    return (
        <Box flex={1} justifyContent="center" padding={4}>
            <Text fontSize="2xl" textAlign="center" marginBottom={4}>註冊</Text>
            <Input placeholder="使用者名字" value={username} onChangeText={setUsername} marginBottom={3} />
            <Input placeholder="信箱" value={email} onChangeText={setEmail} marginBottom={3} />
            <Input placeholder="密碼" type="password" value={password} onChangeText={setPassword} marginBottom={3} />
            <Input placeholder="確認密碼" type="password" value={confirmPassword} onChangeText={setConfirmPassword} marginBottom={3} />
            <Input placeholder="帳號" value={account} onChangeText={setAccount} marginBottom={3} />
            <Button onPress={handleRegister}>註冊</Button>
            <Button onPress={() => navigation.navigate('Login')} marginTop={3}>返回登入</Button>
        </Box>
    );
};

export default Register;

完成

這樣就完成了 Redux Toolkit 的整合以及註冊 API 的改寫。現在你可以使用 Redux 來管理註冊的狀態和錯誤信息。

接著按下套用,Cusor 會幫你修改並套用。接著可以測試看看註冊頁面的行為是否一樣。

一樣可以成功註冊,查看資料庫也會有使用者資訊。

解說

以前如果沒有AI,光自己讀文件到可以設定到可以動真的會需要花很多時間。然後就會放棄。畢竟設置的方法太多種,也常常卡在奇怪的地方做不出來真的會很挫折。

我們來看看 Cursor幫我們做了什麼。

1.安裝 Redux Toolkit 和 React-Redux

首先使用 yarn add @reduxjs/toolkit react-redux 安裝了 Redux Toolkit 和 React-Redux的套件,最一開始沒安裝套件必須安裝。

2. 設定 Redux Store

接下來Cursor在 store.js 中設置了 Redux 的 store,並將我們即將創建的 registerSlice 寫在store裡面。configureStore 是 Redux Toolkit 提供的一個方法,用來簡化 store 的設置。

3. 創建 register Slice

在這步中,我們創建了 registerSlice,這是 Redux Toolkit 的功能,讓我們能夠將 reducer 和 action 一起處理。使用 createAsyncThunk 我們能夠處理 API 的異步請求,並在請求完成後自動更新狀態。

  • 當用戶發送註冊請求時,會透過 registerUser action 發送異步的 API 請求。
  • 如果請求成功,fulfilled 狀態會被觸發並更新應用的狀態;如果失敗,rejected 狀態會被觸發。

4. 包裝應用程式

我們將 Redux store 包在 App.tsx 中,這樣整個應用程式可以使用 Redux 的狀態管理功能。

5. 更新 Register 組件

Register.tsx 中,我們使用了 useDispatchuseSelector 來分發 registerUser action 並讀取錯誤狀態。useEffect 會根據錯誤狀態來顯示錯誤提示或進行下一步操作。

結語

雖然我們成功地請 Cursor幫我們把 redux-toolkit設置好了,也把註冊API功能改寫,但其實他寫的還是不夠好,我們明天會修正一下他寫的部分。還會介紹一下 Redux devTool如何使用。

工商時間

又到了我們最後的工商時間啦~~~。Tom有在經營 Pocast,目前比較偏向職場類的閒聊,未來Podcast預計更新與程式軟體開發相關的主題。有興趣的朋友follow一下。

可以透過以下平台收聽: Apple podcast Spotify KKBOX
Linking Tree


上一篇
[Day15] 讓APP動起來:串接註冊API
下一篇
[DAY17] Redux Debugger
系列文
30天 使用chatGPT輔助學習APP完成接案任務委託30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言